1.else使用
- 语法糖else
1
2
3
4
5
6
7<div *ngIf="isLoggedIn; else loggedOut">
Welcome back, friend.
</div>
<ng-template #loggedOut>
Please friend, login.
</ng-template> - code 等同于下方
1
2
3
4
5
6
7<ng-template [ngIf]="isLoggedIn" [ngIfElse]="loggedOut">
Welcome back, friend.
</ng-template>
<ng-template #loggedOut>
Please friend, login.
</ng-template> - 在高级一点:
1
2
3
4
5
6
7
8
9<ng-container *ngIf="isLoggedIn;then loggedIn;loggedOut">
<ng-template #loggedIn>
Welcome back, friend.
</ng-template>
<ng-template #loggedOut>
Please friend, login.
</ng-template>
<ng-container>
2.ng-show废弃
1 | <div [hidden]="!isLoggedIn"> |
3.angular中使用angularjs组件
- angularjs组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16export const heroDetail = {
bindings: {
hero: '<',
deleted: '&'
},
template: `
<h2>{{$ctrl.hero.name}} details!</h2>
<div><label>id: </label>{{$ctrl.hero.id}}</div>
<button ng-click="$ctrl.onDelete()">Delete</button>
`,
controller: function() {
this.onDelete = () => {
this.deleted(this.hero);
};
}
}; - 定义angular指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import { Directive, ElementRef, Injector, Input, Output, EventEmitter } from '@angular/core';
import { UpgradeComponent } from '@angular/upgrade/static';
import { Hero } from '../hero';
@Directive({
selector: 'hero-detail'
})
export class HeroDetailDirective extends UpgradeComponent {
# 需要对应于angularjs组件定义的数据绑定
@Input() hero: Hero;
@Output() deleted: EventEmitter<Hero>;
constructor(elementRef: ElementRef, injector: Injector) {
super('heroDetail', elementRef, injector);
}
} - 使用指令,引用angularjs组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import { Component } from '@angular/core';
import { Hero } from '../hero';
@Component({
selector: 'my-container',
template: `
<h1>Tour of Heroes</h1>
<hero-detail [hero]="hero"
(deleted)="heroDeleted($event)">
</hero-detail>
`
})
export class ContainerComponent {
hero = new Hero(1, 'Windstorm');
heroDeleted(hero: Hero) {
hero.name = 'Ex-' + hero.name;
}
}
4.常用模板语法
结构指令:
- 列表渲染
1
2
3<li *ngFor="let hero of heroes;">
{{ hero }}
</li> - 列表渲染并展示序号
1
2
3<li *ngFor="let hero of heroes;let i = index;">
{{i+1}} {{ hero }}
</li>推荐添加trackBy,提升性能
1
2
3
4
5
6<li *ngFor="let hero of heroes;trackBy:trackByFn">
{{ hero }}
</li>
trackByFn(index, item) {
return item.id;
} - 条件渲染
1
2
3<li *ngIf="isHidden">
{{ hero }}
</li> - 条件选择
1
2
3
4
5
6<div [ngSwitch]="hero?.emotion">
<app-happy-hero *ngSwitchCase="'happy'" [hero]="hero"></app-happy-hero>
<app-sad-hero *ngSwitchCase="'sad'" [hero]="hero"></app-sad-hero>
<app-confused-hero *ngSwitchCase="'confused'" [hero]="hero"></app-confused-hero>
<app-unknown-hero *ngSwitchDefault [hero]="hero"></app-unknown-hero>
</div>
- 列表渲染
属性指令:
- 数据单向输入
1
2
3
4
5# 动态绑定,更新会触发对应子组件
<app-hero-detail [hero]="currentHero"></app-hero-detail>
# 绑定字符串,非变量值
<app-item-detail childItem="parentItem"></app-item-detail> - 事件反馈
1
2
3
4
5
6
7
8<w-button (click)="handlerClick" />
# 双向数据绑定
<input [(ngModel)]="currentItem.name">
# 等效于
<input [value]="currentItem.name"
(input)="currentItem.name=$event.target.value" > - 属性添加
1
2
3
4
5
6
7<button [attr.aria-label]="help">help</button>
<div [class.special]="isSpecial">Special</div>
<button [style.color]="isSpecial ? 'red' : 'green'">
<button [class]="{foo: true, bar: false}" /> - 自定义双向数据绑定 - x和xChange
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-sizer',
templateUrl: './sizer.component.html',
styleUrls: ['./sizer.component.css']
})
export class SizerComponent {
@Input() size: number | string;
@Output() sizeChange = new EventEmitter<number>();
dec() { this.resize(-1); }
inc() { this.resize(+1); }
resize(delta: number) {
this.size = Math.min(40, Math.max(8, +this.size + delta));
this.sizeChange.emit(this.size);
}
}
# html
<app-sizer [(size)]="fontSizePx"></app-sizer> 等效于
<app-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></app-sizer>
- 数据单向输入
5.ng-template
1 | <div *ngIf="isLoggedIn"> |
6.ng-content
1 | # html |
7.inject 单例模式
- 单例模式
1
2
3
4
5
6
7
8
9import { Injectable } from '@angular/core';
# inject root 利用webpack tree-shaking,优化打包
@Injectable({
providedIn: 'root',
})
export class UserService {
} - privoder配置
1
2
3
4
5@NgModule({
...
providers: [UserService],
...
}) - 如果两者都提供了,那么请注意,从UserService中读取内存数据,可能读取不到!!!
8.angularjs ui-grid问题
- 使用技术升级-ag-grid
- 使用组件升级方案,参考总结3
directive包裹angularjs ui-grid,在angular中使用,但是需要注意一点:已经使用的ui-grid 文件配置,不可以随意修改,否则会影响现有线上的ui展示功能,
譬如:新版本不需要垂直滚动,手动修改默认angularjs ui-grid垂直滚动条配置,那么线上就会导致已有angularjs 表格垂直滚动条不展示,数据展示不全问题。
1
2
3
4
5
6
7
8
9
10
11
12
13 # 我们应该在自己的angular组件中定义配置项,而不应该在通用的配置修改
this.gridOptions = {
enableFiltering: true,
useExternalFiltering: true,
columnDefs: [
{ name: 'name', enableFiltering: false },
{ name: 'gender' },
{ name: 'company', enableFiltering: false}
],
enableHorizontalScrollbar: 1, # 0 关闭,1 开启
enableVerticalScrollbar:0 # 0 关闭,1 开启
}
10.Render2 更新样式 ,ViewChild选取dom
1 | #html |
1 | # render2 api |
11.类计算属性
1 | @Component({ |
12.元素绑定
- @HostBinding()可以为指令的宿主元素添加类、样式、属性等,
- @HostListener()可以监听宿主元素上的事件。
1 | import { Directive, HostBinding, HostListener } from '@angular/core'; |
13.viewChild
- @ViewChild 选择组件模板内的节点, 类型 ElementRef 或子组件
- @ContentChild 选择当前组件引用的子组件 @ContentChild(组件名)
- 区别在于ViewChild选择Shadow DOM, ContentChild 选择 Light DOM,一般情况下用ViewChild就ok了
14.元素宽度
- element clientWidth
内联元素以及没有 CSS 样式的元素的 clientWidth 属性值为 0。Element.clientWidth 属性表示元素的内部宽度,以像素计。该属性包括内边距 padding,但不包括边框 border、外边距 margin 和垂直滚动条(如果有的话)。
当在根元素(元素)上使用clientWidth时(或者在
- jquery width()
始终指内容宽度,不包括border
15.scss 无效
1 | # scss 文件 |
16. httpclient默认json格式接收
1 | # 后端传递text,需要设置类型 |
1 | # 源码 |
17.内存泄漏风险
angular中推荐使用rxjs,进行响应式开发
1 | import { from } from 'rxjs'; |
上述的subscribe代码,在没有unsubscribe情况下,会出现订阅多次情况
我们可以通过多种方法优化
- 1.在OnDestroy中unsubscribe下
1
2
3
4
5public ngOnDestory(){
if( this.$obser){
this.$obser.unsubscribe();
}
} - 2.类似上面写法,只不过参考这段代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# 使用Subscription,一处取消,取消所有的
import { interval } from 'rxjs';
const observable1 = interval(400);
const observable2 = interval(300);
#也可以私用subscription = new Subscription(); //全局创建,然后依次add
const subscription = observable1.subscribe(x => console.log('first: ' + x));
const childSubscription = observable2.subscribe(x => console.log('second: ' + x));
subscription.add(childSubscription);
setTimeout(() => {
// Unsubscribes BOTH subscription and childSubscription
subscription.unsubscribe();
}, 1000); - 3.参考使用rxjs pipe,改用异步
1
2
3
4
5
6
7
8
9
10
11# 具体细节还依赖rxjs operator熟练,后期整理一篇供大家参考
@Component({
selector: 'async-observable-pipe',
template: `<div><code>observable|async</code>:
Time: {{ time$ | async }}</div>`
})
export class AsyncObservablePipeComponent {
time$ = new Observable<string>(observer => {
setInterval(() => observer.next(new Date().toString()), 1000);
});
}
18.组件更新策略调整,优化组件树更新性能
1 | @Component({ |
如下情况会触发OnPush
- @Input值发生改变
- 组件或者子组件,触发Dom事件
- detectChanges 方法调用
- async Pipe 发生改变
19. tooltip显示 getBoundingClientRect
1 | # html |
20 Observable lazy push
1 | #rxjs |
上述代码定义完毕,是否意味着我们就可以获取到数据呢?答案是否定的,请看官网,这样描述的:
To invoke the Observable and see these values, we need to subscribe to it:
code需要如下修改,添加subscribe:
1 | import { Observable } from 'rxjs'; |
那么,同理,Rxjs的所有的operators,我们都需要Subscribe才能调用、获取到值
- 一种方式: subscribe
- 另一种方式,html界面,(count$ | async )
21. Angular httpclient 前端日志搜集失败
1 | # 定义http api请求 |
结合20,我们可以发现,如果不subscribe,http请求是发送不出去的。
尤其在前端记录日志,如果只是定义,没有subscribe,那么前端日志是不能记录成功的哦!!!
22. async 与 subscribe 效果等同
20 页面中导出数据,譬如 export json、csv文件时候,如果使用async 异步设置loading,那么会出现一次导出多份文件情况。
根本原因在于,asyn与subscribe效果等同,多次订阅,没有unsubscribe情况,触发多次保存
23. rxjs 创建数据流同异步之分
同步数据流
- create - Observable 构造函数
- of - 列举有限的
- range - 特定范围
- generate - 循环
- repeat - 重复
- empty - 空
- never - 永不完结
- throw - 抛出错误
异步数据流
- interval - 特定间隔
- timer - 定时
- from
- fromEvent
- ajax
- defer
编写代码时候,常常没有想到异步的存在,导致undefined异常发生。
24. angular iframe loading
我们期望的是,在iframe资源加载完毕,loading结束。
但是我们发现,chrome、safari load会触发两次,firefox、ie11 仅触发一次
1 | # html |
25. Array sort
1 | # 请问,你想到结果是什么吗?是否会发生异常? |
1 | #output |
原因如下:
If compareFunction is supplied, all non-undefined array elements are sorted according to the return value of the compare function (all undefined elements are sorted to the end of the array, with no call to compareFunction)
26. ngrx undefined问题
ngrx 用于状态管理,类似于sessionStorage,有 get/set
1 | # selectors |
我们在selector获取unfined,常见原因有两种:
- 对应的action,没有触发,没有set对应的value
- 对应的value设置了,但是get时候,写法不对,譬如
1 | # 正确的写法 |
27. Cannot find module ‘typescript’
TypeScript is required if you want to compile using ts-node.
1 | npm install -D typescript |
28. ngrx store pip sync or async ?
1 | export const getList = () => |
29 rxjs EMPTY 与Of({})
EMPTY仅仅触发complete,不会触发next。在整合ngrx时,需要谨慎返回。
Of,next先触发,在执行complete
1 | import { fromEvent, interval , of, EMPTY } from 'rxjs'; |
30 rxjs 编写angular export
及时关闭数据流监听
1 | getData: () => { |
更多推荐
Angular8 HttpClient 30分钟深入了解下
Angular Render2你了解吗?
20个你值得了解的Angular开源项目
Angular7/8 read local json的2种方法
Angular8 ui-grid替代方案ag-grid入门
深入了解指令
参考文献
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏